//
// Author: zxc
// Date: 2021/4/15
// Copyright (c) 2021 zxc All rights reserved.
//
// 实现一个基于命令的脚本语言 z语言
//

#include <iostream>
#include <fstream>

using std::ifstream;
using std::cout;
using std::endl;

// 定义命令
// -------
// 打印字符串 参数：字符串
#define PrintStr       "PrintStr"
// 循环打印字符串 参数：字符串 次数
#define PrintStrLoop   "PrintStrLoop"
// 打印空行 参数：无
#define Newline        "Newline"
// 等待按键 参数：无
#define WaitKey        "WaitKey"

constexpr int MAX_LINE_SIZE = 4096;
constexpr int MAX_COMMAND_SIZE = 128;
constexpr int MAX_PARAM_SIZE = 1024;

// 脚本指针
static char** script = nullptr;
// 脚本行数
static int script_size = 0;
// 当前行
static int cur_line = 0;
// 当前字符
static char cur_char = 0;

/**
 * 加载脚本
 * @param path 路径
 */
void LoadScript(const char* path);
/**
 * 执行脚本
 */
void RunScript();
/**
 * 卸载脚本
 */
void UnloadScript();
/**
 * 获取命令
 * @param command 缓存命令
 */
void GetCommand(char* command);
/**
 * 获取str参数
 * @param param 缓存str参数
 */
void GetStrParam(char* param);
/**
 * 获取int参数
 * @return int参数
 */
int GetIntParam();

int main(int argc, char** argv)
{
    if(argc == 1)
    {
        cout << "Please input a zscript file" << endl;
        return -1;
    }
    char* filePath = argv[1];
    LoadScript(filePath);
    RunScript();
    UnloadScript();

    return 0;
}

void LoadScript(const char* path)
{
    ifstream ifs(path);

    // 计算行数
    while(ifs.peek() != EOF)
    {
        // 根据换行符确定行数
        if(ifs.get() == '\n')
        {
            ++script_size;
        }
    }
    // 最后一行没有换行，这里补上
    ++script_size;

    script = new char*[script_size];
    // 指针移动到头部
    ifs.seekg(0, ifstream::beg);
    // 填充script内容
    for(int i = 0; i < script_size; ++i)
    {
        char* line = new char[MAX_LINE_SIZE];
        ifs.getline(line, MAX_LINE_SIZE);
        script[i] = line;
    }
}

void RunScript()
{
    for(cur_line = 0; cur_line < script_size; ++cur_line)
    {
        // 重置
        cur_char = 0;
        char command[MAX_COMMAND_SIZE];
        char strParam[MAX_PARAM_SIZE];

        GetCommand(command);
        if(strcmp(command, PrintStr) == 0)
        {
            GetStrParam(strParam);
            cout << strParam << endl;
        }
        else if(strcmp(command, PrintStrLoop) == 0)
        {
            GetStrParam(strParam);
            int count = GetIntParam();
            for(int i = 0; i < count; ++i)
            {
                cout << "\t" << i << ": " << strParam << endl;
            }
        }
        else if(strcmp(command, Newline) == 0)
        {
            cout << "\n" << endl;
        }
        else if(strcmp(command, WaitKey) == 0)
        {
            getchar();
        }
        else
        {
            cout << "Error!!: Invalid Command: " << command << endl;
        }
    }
}

void UnloadScript()
{
    if(!script)
    {
        return;
    }

    // 释放每一行
    for(int i = 0; i < script_size; ++i)
    {
        delete script[i];
        script[i] = nullptr;
    }
    // 释放自己
    delete script;
    script = nullptr;
}

void GetCommand(char* command)
{
    char* line = script[cur_line];
    int size = strlen(line);
    int cur = 0;
    // 读到第一个空格或者换行为止
    while(cur_char < size)
    {
        if(line[cur_char] == ' ' ||
            line[cur_char] == '\n')
        {
            break;
        }
        command[cur] = line[cur_char];
        ++cur;
        ++cur_char;
    }
    command[cur] = '\0';
    // 跳过空格
    ++cur_char;
}

void GetStrParam(char* param)
{
    char* line = script[cur_line];
    int size = strlen(line);
    // 提取引号中的内容
    // 跳过前引号
    ++cur_char;
    int cur = 0;
    // 读到引号为止
    while(cur_char < size)
    {
        if(line[cur_char] == '\"')
        {
            break;
        }
        param[cur] = line[cur_char];
        ++cur;
        ++cur_char;
    }
    param[cur] = '\0';
    // 跳过引号和空格
    cur_char+=2;
}

int GetIntParam()
{
    char* line = script[cur_line];
    int size = strlen(line);
    char intStr[MAX_PARAM_SIZE];
    int cur = 0;
    // 读到空格或者换行为止
    while(cur_char < size)
    {
        if(line[cur_char] == ' ' ||
            line[cur_char] == '\n')
        {
            break;
        }
        intStr[cur] = line[cur_char];
        ++cur;
        ++cur_char;
    }
    // 加上一个结束符方便转换
    intStr[cur] = '\0';
    // 跳过空格
    ++cur_char;

    return strtol(intStr, nullptr, 10);
}